﻿using System.Collections.Generic;

using Vestras.StarCraft2.Luna.Parser;
using Vestras.StarCraft2.Luna.Parser.Ast;

namespace Vestras.StarCraft2.Luna.Validation {
    public sealed class VariableTypeValidator : ITypeValidator {
        public System.Type[] Type {
            get {
                return new[] { typeof(Variable) };
            }
        }

        public bool Validate(object value) {
            if (value != null && value is Variable) {
                ErrorSink.UpdateChangeCollection();
                Variable variable = value as Variable;
                IList<Entity> types = new List<Entity>(variable.Type.GetReferencedTypes());
                if (types.Count == 0) {
                    ErrorSink.AddError(ErrorSink.Error.Create("Cannot find type '" + variable.Type.GetQualifiedId() + "'", variable.Type));
                    return false;
                }

                string ambiguityEntities;
                if (types.IsAmbiguous(out ambiguityEntities)) {
                    ErrorSink.AddError(ErrorSink.Error.Create("Ambiguity between types " + ambiguityEntities, variable.Type));
                    return false;
                }

                Entity typeEntity = types[0];
                if (typeEntity is Struct) {
                    Struct s = typeEntity as Struct;
                    if (s.Parent == null) {
                        if (s.FileName == variable.FileName) {
                            if (s.OrderIndex > variable.OrderIndex) {
                                ErrorSink.AddError(ErrorSink.Error.Create("Cannot access struct '" + s.Name + "' because it has not yet been declared", variable.Type));
                                return false;
                            }
                        } else {
                            if (s.FileName != null && !s.FileName.IsIncludedByFile(variable.FileName)) {
                                ErrorSink.AddError(ErrorSink.Error.Create("Cannot find struct '" + s.Name + "'", variable.Type));
                            }
                        }
                    }
                } else if (typeEntity is Typedef) {
                    Typedef typedef = typeEntity as Typedef;
                    if (typedef.Parent == null) {
                        if (typedef.FileName == variable.FileName) {
                            if (typedef.OrderIndex > variable.OrderIndex) {
                                ErrorSink.AddError(ErrorSink.Error.Create("Cannot access typedef '" + typedef.CreatedType.GetQualifiedId() + "' because it has not yet been declared", variable.Type));
                                return false;
                            }
                        } else {
                            if (!typedef.FileName.IsIncludedByFile(variable.FileName)) {
                                ErrorSink.AddError(ErrorSink.Error.Create("Cannot find typedef '" + typedef.CreatedType.GetQualifiedId() + "'", variable.Type));
                            }
                        }
                    }
                }

                if (variable.Value != null && !variable.Value.DoesExpressionResolveToType(variable.Type) && !variable.Value.IsNull()) {
                    ErrorSink.AddError(ErrorSink.Error.Create("Cannot convert type '" + variable.Value.GetTypeQualifiedId() + "' to type '" + variable.Type.GetTypeQualifiedId() + "'", variable.Value));
                    return false;
                }

                if (variable.Parent != null) {
                    if (variable.Modifiers != Modifiers.None && variable.Modifiers != Modifiers.Const) {
                        ErrorSink.AddError(ErrorSink.Error.Create("A local variable cannot have modifiers.", variable));
                        return false;
                    }
                }

                return true;
            }

            return false;
        }
    }
}
